package org.stbernardpreserve.learning.effectivejava.hashcodeequalstostring;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

public class ApacheDog {

	public static enum Breed {
		STBERNARD {
			@Override
			public String toString() {
				return "St. Bernard";
			}
		},
		ROTTWEILER {
			@Override
			public String toString() {
				return "Rottweiler";
			}
		},
		OLD_ENGLISH_MASTIFF {
			@Override
			public String toString() {
				return "Old English Mastiff";
			}
		}

	}

	@SuppressWarnings("serial")
	public static class IllegalDogStateException extends RuntimeException {

		public IllegalDogStateException(String message) {
			super(message);
		}

	}

	private final int weight;
	private final int age;
	private final String name;
	private final Breed breed;
	private final double droolCoefficient;

	public ApacheDog(DogBuilder dogBuilder) {
		if (dogBuilder.weight < 1)
			throw new IllegalDogStateException("Weight must be greater than 0");
		if (dogBuilder.age < 1)
			throw new IllegalDogStateException("Age must be greater than 0");
		if (dogBuilder.breed == null)
			throw new IllegalDogStateException("Breed must be assigned");

		weight = dogBuilder.weight;
		age = dogBuilder.age;
		name = dogBuilder.name;
		breed = dogBuilder.breed;
		droolCoefficient = dogBuilder.droolCoefficient;
	}

	public int getWeight() {
		return weight;
	}

	public int getAge() {
		return age;
	}

	public String getName() {
		return name;
	}

	public Breed getBreed() {
		return breed;
	}

	public double getDroolCoefficient() {
		return droolCoefficient;
	}

	@Override
	public String toString() {
		return new ToStringBuilder(this, new ApacheDogToStringStyle())
				.append("breed", breed).append("name", name).append("age", age)
				.append("weight", weight).append("droolWeight", droolWeight())
				.toString();
	}

	@SuppressWarnings("serial")
	private static class ApacheDogToStringStyle extends ToStringStyle {
		private ApacheDogToStringStyle() {
			setDefaultFullDetail(true);
			this.setUseIdentityHashCode(false);
			this.setUseClassName(false);
		}

		protected void appendDetail(StringBuffer buffer, String fieldName,
				Object value) {

			buffer.append(value);
		}
	}

	@Override
	public boolean equals(Object o) {
		if (o instanceof ApacheDog == false)
			return false;

		if (this == o)
			return true;

		ApacheDog dog = (ApacheDog) o;

		return new EqualsBuilder().append(dog.breed, breed)
				.append(age, dog.age).append(weight, dog.weight).isEquals();
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder(17, 37).append(breed).append(age)
				.append(weight).toHashCode();
	}

	private String droolWeight() {
		if (droolCoefficient < .3)
			return "light";
		if (droolCoefficient < .6)
			return "medium";
		return "heavy";

	}

	public static class DogBuilder {

		private int weight;
		private int age;
		private String name = "";
		private Breed breed;
		private double droolCoefficient = 0;

		public DogBuilder weight(int weight) {
			this.weight = weight;
			return this;
		}

		public DogBuilder age(int age) {
			this.age = age;
			return this;
		}

		public DogBuilder name(String name) {
			this.name = name;
			return this;
		}

		public DogBuilder breed(Breed breed) {
			this.breed = breed;
			return this;
		}

		public DogBuilder droolCoefficient(double droolCoefficient) {
			this.droolCoefficient = droolCoefficient;
			return this;
		}

		public ApacheDog build() {
			return new ApacheDog(this);
		}
	}

}
